package dao

import (
	"context"
	"fmt"

	"wxpay/internal/model"
	"wxpay/pkg/mcontext"
	"wxpay/pkg/merrors"

	"go.uber.org/zap"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type LockI interface {
	LockTrade(ctx context.Context, tx *gorm.DB, tradeID int64) (*model.Trade, error)
	LockMessage(ctx context.Context, tx *gorm.DB, msgId int64) (*model.Message, error)
	LockRefund(ctx context.Context, tx *gorm.DB, refundID int64) (*model.Refund, error)
	LockTransfer(ctx context.Context, tx *gorm.DB, transferID int64) (*model.Transfer, error)
	LockSubMchWithdraw(ctx context.Context, tx *gorm.DB, withdrawID int64) (*model.SubMchWithdraw, error)
	LockApplyment(ctx context.Context, tx *gorm.DB, applyID int64) (*model.Applyment, error)
	LockProfitOrder(ctx context.Context, tx *gorm.DB, pftID int64) (*model.Profitshare, error)
	LockProfitReturn(ctx context.Context, tx *gorm.DB, pftID int64) (*model.Profitreturn, error)
	LockAliFaceToFaceSign(ctx context.Context, tx *gorm.DB, applyID int64) (*model.AlipayAgentSign, error)
}

type LockRepo struct {
	*gorm.DB
}

func NewLockRepo(d *gorm.DB) *LockRepo {
	return &LockRepo{d}
}

func (d *LockRepo) LockTrade(ctx context.Context, tx *gorm.DB, tradeID int64) (*model.Trade, error) {
	const op merrors.Op = "dao.LockTrade"
	l := mcontext.Logger(ctx)
	t := model.Trade{}
	if er := tx.Model(&model.Trade{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", tradeID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取trade加锁失败，tradeID:%d", tradeID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil

}

func (d *LockRepo) LockMessage(ctx context.Context, tx *gorm.DB, msgId int64) (*model.Message, error) {
	const op merrors.Op = "dao.LockMessage"
	l := mcontext.Logger(ctx)
	msg := model.Message{}
	if er := tx.Model(&model.Message{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", msgId).First(&msg).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		l.Info(fmt.Sprintf("获取message加锁失败，msgId:%d", msgId), zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, fmt.Sprintf("获取message加锁失败，msgId:%d", msgId), merrors.ErrAcquireLock)
	}
	return &msg, nil
}

func (d *LockRepo) LockRefund(ctx context.Context, tx *gorm.DB, refundID int64) (*model.Refund, error) {
	const op merrors.Op = "dao.LockRefund"
	l := mcontext.Logger(ctx)
	t := model.Refund{}
	if er := tx.Model(&model.Refund{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", refundID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取refund加锁失败，refundID:%d", refundID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil

}

func (d *LockRepo) LockTransfer(ctx context.Context, tx *gorm.DB, transferID int64) (*model.Transfer, error) {
	const op merrors.Op = "dao.LockTransfer"
	l := mcontext.Logger(ctx)
	t := model.Transfer{}
	if er := tx.Model(&model.Transfer{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", transferID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取transfer加锁失败，transferID:%d", transferID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil

}

func (d *LockRepo) LockSubMchWithdraw(ctx context.Context, tx *gorm.DB, withdrawID int64) (*model.SubMchWithdraw, error) {
	const op merrors.Op = "dao.LockSubMchWithdraw"
	l := mcontext.Logger(ctx)
	t := model.SubMchWithdraw{}
	if er := tx.Model(&model.SubMchWithdraw{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", withdrawID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取submch_withdraw加锁失败，transferID:%d", withdrawID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil
}

func (d *LockRepo) LockApplyment(ctx context.Context, tx *gorm.DB, applyID int64) (*model.Applyment, error) {
	const op merrors.Op = "dao.LockApplyment"
	l := mcontext.Logger(ctx)
	t := model.Applyment{}
	if er := tx.Model(&model.Applyment{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", applyID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取applyment加锁失败，applyID:%d", applyID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil
}

func (d *LockRepo) LockProfitOrder(ctx context.Context, tx *gorm.DB, pftID int64) (*model.Profitshare, error) {
	const op merrors.Op = "dao.LockProfitOrder"
	l := mcontext.Logger(ctx)
	t := model.Profitshare{}
	if er := tx.Model(&model.Profitshare{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", pftID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取profitshare加锁失败，ID:%d", pftID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil
}

func (d *LockRepo) LockProfitReturn(ctx context.Context, tx *gorm.DB, pftID int64) (*model.Profitreturn, error) {
	const op merrors.Op = "dao.LockProfitReturn"
	l := mcontext.Logger(ctx)
	t := model.Profitreturn{}
	if er := tx.Model(&model.Profitreturn{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", pftID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取profitreturn加锁失败，ID:%d", pftID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil
}

func (d *LockRepo) LockAliFaceToFaceSign(ctx context.Context, tx *gorm.DB, applyID int64) (*model.AlipayAgentSign, error) {
	const op merrors.Op = "dao.LockAliFaceToFaceSign"
	l := mcontext.Logger(ctx)
	t := model.AlipayAgentSign{}
	if er := tx.Model(&model.AlipayAgentSign{}).Clauses(clause.Locking{Strength: "UPDATE"}).Where("id = ?", applyID).First(&t).Error; er != nil && er == gorm.ErrRecordNotFound {
		return nil, merrors.E(op, merrors.NotExist, er.Error(), merrors.ErrNotFound)
	} else if er != nil {
		message := fmt.Sprintf("获取ali_facettoface_sign加锁失败，applyID:%d", applyID)
		l.Info(message, zap.Error(er))
		return nil, merrors.E(op, merrors.Invalid, message, merrors.ErrAcquireLock)
	}
	return &t, nil
}
